package com.rain.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.RandomUtil;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.rain.ai.AiCodeGenTypeRoutingService;
import com.rain.codemother.innderservie.innerScreenshotService;
import com.rain.codemother.innderservie.innerUserService;
import com.rain.common.*;
import com.rain.core.AiCodeGeneratorFacade2;
import com.rain.exception.BusinessException;
import com.rain.exception.ErrorCode;
import com.rain.exception.ThrowUtils;
import com.rain.mapper.AppMapper;
import com.rain.dto.app.AppAddRequest;
import com.rain.dto.app.AppQueryRequest;
import com.rain.entity.App;
import com.rain.entity.User;
import com.rain.enums.ChatHistoryMessageTypeEnum;
import com.rain.enums.CodeGenTypeEnum;
import com.rain.vo.AppVo;
import com.rain.vo.UserVo;
import com.rain.service.AppService;
import com.rain.service.ChatHistoryService;
import com.rain.service.UserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Flux;
import com.rain.constant.AppConstant;
import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AppServiceImpl extends ServiceImpl<AppMapper, App> implements AppService {

    @Resource
    private innerUserService innerUserService;
    @Resource
    @Lazy
    private AiCodeGeneratorFacade2 aiCodeGeneratorFacade2;
    @Lazy
    @Resource
    private ChatHistoryService chatHistoryService;
    @Resource
    @Lazy
    private innerScreenshotService innerScreenshotService;
    @Resource
    private AiCodeGenTypeRoutingService aiCodeGenTypeRoutingService;

    @Override
    public AppVo getAppVo(App app) {
        if(app==null){
            return null;
        }
        AppVo appVo = new AppVo();
        BeanUtils.copyProperties(app,appVo);
        Long userId = app.getUserId();
        User user = innerUserService.getById(userId);
        UserVo userVo = innerUserService.getUserVO(user);
        appVo.setUserVo(userVo);
        return appVo;
    }

    @Override
    public List<AppVo> getAppVoList(List<App> appList) {
        if(CollectionUtils.isEmpty(appList)){
            return null;
        }
        Set<Long> userIdSet = appList.stream().map(App::getUserId)
                .collect(Collectors.toSet());
        Map<Long, UserVo> userVOMap = innerUserService.listByIds(userIdSet).stream()
                .collect(Collectors.toMap(User::getId, innerUserService::getUserVO));
        List<AppVo> appVoList = appList.stream().map(app -> {
            AppVo appVo = new AppVo();
            BeanUtils.copyProperties(app, appVo);
            Long userId = app.getUserId();
            if (userId != null) {
                UserVo userVo = userVOMap.get(userId);
                appVo.setUserVo(userVo);
            }
            return appVo;
        }).collect(Collectors.toList());
        return appVoList;
    }

    /**
     * 删除应用时，关联删除对话历史
     *
     * @param id
     * @return
     */
    @Override
    public boolean removeById(Serializable id) {
        if (id == null) {
            return false;
        }
        long appId = Long.parseLong(id.toString());
        if (appId <= 0) {
            return false;
        }
        // 先删除关联的对话历史
        try {
            chatHistoryService.deleteByAppId(appId);
        } catch (Exception e) {
            log.error("删除应用关联的对话历史失败：{}", e.getMessage());
        }
        // 删除应用
        return super.removeById(id);
    }


    @Override
    public Long addApp(AppAddRequest appAddRequest, User loginUser) {
        String initPrompt = appAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StringUtils.isBlank(initPrompt), ErrorCode.PARAMS_ERROR);
        App app = new App();
        BeanUtils.copyProperties(appAddRequest,app);
        app.setUserId(loginUser.getId());
        app.setAppName(initPrompt.substring(0,Math.min(initPrompt.length(),12)));
//        app.setCodeGenType(CodeGenTypeEnum.MULTI_FILE.getValue());
//        使用 AI 智能选择代码生成类型
        CodeGenTypeEnum codeGenTypeEnum = aiCodeGenTypeRoutingService.routeCodeGenType(initPrompt);
        app.setCodeGenType(codeGenTypeEnum.getValue());
        boolean save = this.save(app);
        ThrowUtils.throwIf(!save,ErrorCode.PARAMS_ERROR);
        log.info("成功创建应用，Id:{},类型：{}",app.getId(),codeGenTypeEnum.getValue());
        return app.getId();
    }


    @Override
    public QueryWrapper getQueryWrapper(AppQueryRequest appQueryRequest) {
        ThrowUtils.throwIf(appQueryRequest==null, ErrorCode.PARAMS_ERROR);
        Long id = appQueryRequest.getId();
        String appName = appQueryRequest.getAppName();
        String cover = appQueryRequest.getCover();
        String initPrompt = appQueryRequest.getInitPrompt();
        String codeGenType = appQueryRequest.getCodeGenType();
        String deployKey = appQueryRequest.getDeployKey();
        Integer priority = appQueryRequest.getPriority();
        Long userId = appQueryRequest.getUserId();
        String sortField = appQueryRequest.getSortField();
        String sortOrder = appQueryRequest.getSortOrder();
        return QueryWrapper.create()
                .eq("id", id)
                .like("appName", appName)
                .like("cover", cover)
                .like("initPrompt", initPrompt)
                .eq("codeGenType", codeGenType)
                .eq("deployKey", deployKey)
                .eq("priority", priority)
                .eq("userId", userId)
                .orderBy(sortField, "ascend".equals(sortOrder));
    }

    @Override
    public Flux<String> chatToGenCode(String message, Long appId, User loginUser) {
        ThrowUtils.throwIf(appId == null || appId < 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(StringUtils.isBlank(message), ErrorCode.PARAMS_ERROR);
        App app = this.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR);
        if (!loginUser.getId().equals(app.getUserId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        String codeGenType = app.getCodeGenType();
        CodeGenTypeEnum codeGenTypeEnum = CodeGenTypeEnum.getEnumByValue(codeGenType);
        if (codeGenTypeEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "codeGenType is not valid");
        }
//        return aiCodeGeneratorFacade2.generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
//        调用ai生成代码前，先将用户信息保存到历史
        chatHistoryService.addChatHistory(appId, loginUser.getId()
                , message, ChatHistoryMessageTypeEnum.USER.getValue());
//        调用ai生成代码
        Flux<String> contentFlux = aiCodeGeneratorFacade2.
                generateAndSaveCodeStream(message, codeGenTypeEnum, appId);
        StringBuilder aiResponeBuilder = new StringBuilder();
        return contentFlux.map(chuck -> {
            aiResponeBuilder.append(chuck);
            return chuck;
        }).doOnCancel(() -> {
            // 流式返回完成后，保存 AI 消息到对话历史中
            String aiResponseStr = aiResponeBuilder.toString();
            if (StringUtils.isNotBlank(aiResponseStr)) {
                chatHistoryService.addChatHistory(appId, loginUser.getId()
                        , aiResponseStr, ChatHistoryMessageTypeEnum.AI.getValue());
            }
        }).doOnError(error -> {
            // 如果 AI 回复失败，也需要保存记录到数据库中
            String errorMessage = "AI回复失败" + error.getMessage();
            chatHistoryService.addChatHistory(appId, loginUser.getId(),
                    errorMessage, ChatHistoryMessageTypeEnum.AI.getValue());
        });

    }

    @Override
    public String deployApp(Long appId, User loginUser) {
//        1,参数校验
        ThrowUtils.throwIf(appId==null|| appId<0,ErrorCode.PARAMS_ERROR,"appId is null or less than 0");
        ThrowUtils.throwIf(loginUser==null,ErrorCode.PARAMS_ERROR,"用户未登录");
        App app = this.getById(appId);
        ThrowUtils.throwIf(app==null,ErrorCode.PARAMS_ERROR);
        //权限校验
        if(!loginUser.getId().equals(app.getUserId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
//        2,如果没有deployKey，则生成一个6位的
        String deployKey = app.getDeployKey();
        if(StringUtils.isBlank(deployKey)){
            deployKey = RandomUtil.randomString(6);
        }
//        3，获取原始代码生成路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        File sourceDir = new File(sourceDirPath);
        if(!sourceDir.exists()||!sourceDir.isDirectory()){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"应用代码路径不存在，请先生成应用");
        }
//        4,复制文件到部署目录
        String deployDirPath = AppConstant.CODE_DEPLOY_ROOT_DIR + File.separator + deployKey;
        try {
            FileUtil.copyContent(sourceDir, new File(deployDirPath),true);
        }catch (Exception e){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"应用部署失败");
        }
//        5,跟新数据库
        App updateApp = new App();
        updateApp.setId(appId);
        updateApp.setDeployedTime(LocalDateTime.now());
        updateApp.setDeployKey(deployKey);
        boolean update = this.updateById(updateApp);
        ThrowUtils.throwIf(!update,ErrorCode.PARAMS_ERROR);
//        return String.format("%s/%s",AppConstant.CODE_DEPLOY_HOST,deployKey);
        String appDeployUrl  = String.format("%s/%s", AppConstant.CODE_DEPLOY_ROOT_DIR, deployKey);
        generateAppScreenshotAsync(appId,appDeployUrl);
        return appDeployUrl;
    }

    private void generateAppScreenshotAsync(Long appId, String appDeployUrl) {
        Thread.startVirtualThread(()->{
//            调用截图服务，生成截图，并上传
            String screenshotUrl  = innerScreenshotService.generateAndUploadScreenshot(appDeployUrl);
            App app = new App();
            app.setId(appId);
            app.setCover(screenshotUrl);
            boolean updated = this.updateById(app);
            ThrowUtils.throwIf(!updated,ErrorCode.OPERATION_ERROR);
        });

    }
}
